# -*- Mode: makefile -*- 

# Makefile for the cil wrapper
# @do_not_edit@ Makefile.in
#
# author: George Necula

#
# If you want to add extra CIL features, you do not always need to change 
# this file. Just invoke 
#        ./configure EXTRASRCDIRS=/home/foodir EXTRAFEATURES="foo bar"
# This will add two features that must be defined in foo.ml and bar.ml
# 

# Debugging. Set ECHO= to debug this Makefile 
.PHONY: setup quickbuild doc distrib machdep cilversion
ECHO = @

# It is important to build quickbuild first,to generate the proper dependencies
all:  quickbuild setup

# Now add the defines for the CIL features
include Makefile.features

# look out for outdated Makefile; if it's out of date, this will automatically
# re-run ./config.status, then re-exec make with the same arguments
Makefile: config.status Makefile.in 
	./$<

config.status: configure
	./$@ --recheck

configure: configure.in aclocal.m4
	autoconf

_GNUCC=1



ifdef RELEASE
  NATIVECAML := 1
  UNSAFE     := 1
endif

ifndef ARCHOS
   ARCHOS=x86_DARWIN
endif

ifndef MINGW
   MINGW=
endif

ifndef FLEXLINK
   FLEXLINK=
endif

ifndef EMUL
   EMUL=
endif
ifdef EMUL
	 # Compile machdep-ml.c with -static when
	 # cross-compiling only (because -static
	 # is broken on MacOS X)
   MACHDEPCC=$(CC) $(CFLAGS) -static -D_GNUCC
else
   MACHDEPCC=gcc -D_GNUCC
endif

# Put here all the byproducts of make
OBJDIR      := obj/$(ARCHOS)
DEPENDDIR   := obj/.depend

CILLY_FEATURES :=
ifdef USE_BLOCKINGGRAPH
  CILLY_FEATURES += blockinggraph
endif
ifdef USE_ZRAPP
  CILLY_FEATURES += rmciltmps zrapp
endif
ifdef USE_LLVM
  CILLY_FEATURES += llvmutils llvmgen llvmssa llvm
endif
# Add the EXTRAFEATURES
CILLY_FEATURES += 

    # Now rules to make cilly
CILLY_LIBRARY_MODULES = pretty inthash errormsg alpha trace stats util clist \
              cilutil escape longarray growArray\
              cabs cabshelper cabsvisit whitetrack cprint lexerhack machdep machdepenv cparser clexer  \
              cilversion cilint cil cillower formatparse formatlex formatcil cabs2cil \
              patch frontc check mergecil \
              dataflow dominators bitmap ssa ciltools \
              usedef logcalls logwrites rmtmps \
	      callgraph epicenter heapify \
              setp uref olf ptranal \
              canonicalize heap oneret partial simplemem simplify \
	      dataslicing sfi expcompare\
              cfg liveness reachingdefs deadcodeelim availexps \
              availexpslv predabst\
              testcil \
	      $(CILLY_FEATURES) \
	      ciloptions feature_config
# ww: we don't want "main" in an external cil library (cil.cma),
# otherwise every program that links against that library will get
# main's argument checking and whatnot ...
CILLY_MODULES = $(CILLY_LIBRARY_MODULES) main
CILLY_LIBS     = unix str nums

SOURCEDIRS  += src src/frontc src/ext src/ext/pta ocamlutil 
MLLS        += clexer.mll formatlex.mll
MLYS        += cparser.mly formatparse.mly
MODULES     += $(CILLY_MODULES)

BEFOREDEPS += $(OBJDIR)/machdep.ml
BEFOREDEPS += $(OBJDIR)/cilversion.ml
BEFOREDEPS += $(OBJDIR)/feature_config.ml

    # Include now the common set of rules for OCAML
include ocamlutil/Makefile.ocaml

OBEXEEXT = $(EXEEXT:asm=native)
IGNOREDIRS = obj,doc,test
ocamlbuild:
	ocamlbuild -cflags "$(COMPILE_FLAGS)" -lflags "$(LINK_FLAGS)" \
	-Xs $(IGNOREDIRS) -no-links src/main$(OBEXEEXT) src/cil.$(CMXA)
	cp _build/src/main$(OBEXEEXT) $(OBJDIR)/cilly$(EXE)
	cp _build/src/cil.$(CMXA) $(OBJDIR)/

   # Now the rule to make cilly

cilly: $(OBJDIR)/cilly$(EXE)
$(OBJDIR)/cilly$(EXE) : $(CILLY_MODULES:%=$(OBJDIR)/%.$(CMO))
	@$(NARRATIVE) "Linking $(COMPILETOWHAT) $@ $(LINKMSG)"
	$(AT)$(CAMLLINK) -verbose -o $@ \
                    $(CILLY_LIBS:%=%.$(CMXA)) \
                    $^


quickbuild: cilversion machdep cilly lib/Cilly.pm

# Setup also makes the native code versions

setup: cilversion machdep
	$(MAKE) cilly NATIVECAML= 
	$(MAKE) cilly NATIVECAML=1
	$(MAKE) cillib NATIVECAML=
	$(MAKE) cillib NATIVECAML=1

# Create the machine dependency module
# If the cl command cannot be run then the MSVC part will be identical to GCC
.PHONY : machdep 
machdep: $(OBJDIR)/machdep.ml 
$(OBJDIR)/machdep.ml : src/machdep-ml.c configure.in Makefile.in
	rm -f $@
	mkdir -p $(OBJDIR)
	echo "(* This module was generated automatically by code in Makefile and $(<F) *)" >$@
# Now generate the type definition
	echo "type mach = {" >> $@ 
	echo "  version_major: int;     (* Major version number *)"    >> $@
	echo "  version_minor: int;     (* Minor version number *)"    >> $@
	echo "  version: string;        (* gcc version string *)"      >> $@
	echo "  underscore_name: bool;  (* If assembly names have leading underscore *)" >> $@
	echo "  sizeof_short: int;      (* Size of \"short\" *)"       >> $@
	echo "  sizeof_int: int;        (* Size of \"int\" *)"         >> $@
	echo "  sizeof_bool: int;       (* Size of \"_Bool\" *)"       >> $@
	echo "  sizeof_long: int ;      (* Size of \"long\" *)"        >> $@
	echo "  sizeof_longlong: int;   (* Size of \"long long\" *)"   >> $@
	echo "  sizeof_ptr: int;        (* Size of pointers *)"        >> $@
	echo "  sizeof_float: int;      (* Size of \"float\" *)"       >> $@
	echo "  sizeof_double: int;     (* Size of \"double\" *)"      >> $@
	echo "  sizeof_longdouble: int; (* Size of \"long double\" *)" >> $@
	echo "  sizeof_void: int;       (* Size of \"void\" *)"        >> $@
	echo "  sizeof_fun: int;        (* Size of function *)"        >> $@
	echo "  size_t: string;         (* Type of \"sizeof(T)\" *)"   >> $@
	echo "  wchar_t: string;        (* Type of \"wchar_t\" *)"     >> $@ 
	echo "  alignof_short: int;     (* Alignment of \"short\" *)"  >> $@
	echo "  alignof_int: int;       (* Alignment of \"int\" *)"    >> $@
	echo "  alignof_bool: int;      (* Alignment of \"_Bool\" *)"    >> $@
	echo "  alignof_long: int;      (* Alignment of \"long\" *)"   >> $@
	echo "  alignof_longlong: int;  (* Alignment of \"long long\" *)" >> $@
	echo "  alignof_ptr: int;       (* Alignment of pointers *)"   >> $@
	echo "  alignof_enum: int;      (* Alignment of enum types *)" >> $@
	echo "  alignof_float: int;     (* Alignment of \"float\" *)"  >> $@
	echo "  alignof_double: int;    (* Alignment of \"double\" *)" >> $@
	echo "  alignof_longdouble: int;  (* Alignment of \"long double\" *)" >> $@
	echo "  alignof_str: int;       (* Alignment of strings *)" >> $@
	echo "  alignof_fun: int;       (* Alignment of function *)" >> $@
	echo "  alignof_aligned: int;   (* Alignment of anything with the \"aligned\" attribute *)" >> $@
	echo "  char_is_unsigned: bool; (* Whether \"char\" is unsigned *)">> $@
	echo "  const_string_literals: bool; (* Whether string literals have const chars *)">> $@
	echo "  little_endian: bool; (* whether the machine is little endian *)">>$@
	echo "  __thread_is_keyword: bool; (* whether __thread is a keyword *)">>$@
	echo "  __builtin_va_list: bool; (* whether __builtin_va_list is builtin (gccism) *)">>$@
	echo "}" >> $@ 	
	if $(MACHDEPCC) $< -o $(OBJDIR)/machdep-ml.exe ;then \
	    echo "machdep-ml.exe created succesfully." \
	;else \
            rm -f $@; exit 1 \
        ;fi 
	echo "let gcc = {" >>$@
	$(EMUL) $(OBJDIR)/machdep-ml.exe >>$@
	echo "}"          >>$@
	if cl /D_MSVC $< /Fe$(OBJDIR)/machdep-ml.exe /Fo$(OBJDIR)/machdep-ml.obj ;then \
           echo "let hasMSVC = true" >>$@ ;\
	         echo "let msvc = {" >>$@ ;\
	           $(OBJDIR)/machdep-ml.exe >>$@ ;\
	         echo "}"          >>$@ \
        ;else \
           echo "let hasMSVC = false" >>$@ ;\
					 echo "let msvc = gcc" >> $@ \
			  ;fi
	echo "let theMachine : mach ref = ref gcc" >>$@


lib/Cilly.pm: lib/Cilly.pm.in src/machdep-ml.c configure.in Makefile.in
	cp lib/Cilly.pm.in lib/Cilly.pm
	if $(MACHDEPCC) -m32 src/machdep-ml.c -o $(OBJDIR)/machdep-ml32.exe ;then \
	  sed -i -e "s|nogcc32model|`$(EMUL) $(OBJDIR)/machdep-ml32.exe --env`|" lib/Cilly.pm; \
	fi
	if $(MACHDEPCC) -m64 src/machdep-ml.c -o $(OBJDIR)/machdep-ml64.exe ;then \
	  sed -i -e "s|nogcc64model|`$(EMUL) $(OBJDIR)/machdep-ml64.exe --env`|" lib/Cilly.pm; \
	fi

# Create the version information module
.PHONY: cilversion
cilversion: $(OBJDIR)/cilversion.ml
$(OBJDIR)/cilversion.ml: src/cilversion.ml.in config.status
	rm -f $@
	./config.status --file=$@:$<

# build library
.PHONY: cillib
cillib: $(OBJDIR)/cil.$(CMXA) lib/Cilly.pm

$(OBJDIR)/feature_config.ml: config.status
	rm -f $(OBJDIR)/feature_config.*
	echo "(* This module was generated automatically by code in Makefile.in *)" >$@
# The Cilly feature options. A list of Cil.featureDescr 
	echo "open Cil"   >>$@ 
	echo "let features : featureDescr list = [" >> $@
ifdef USE_BLOCKINGGRAPH
	echo "   Blockinggraph.feature;" >> $@  
endif
ifdef USE_RAND
	echo "   Rand.feature;" >> $@  
endif
ifdef USE_ARITHABS
	echo "   Arithabs.feature;" >>$@
endif
ifdef USE_ZRAPP
	echo "   Zrapp.feature;" >> $@  
endif
ifdef USE_LLVM
	echo "   Llvm.feature;" >> $@  
endif
# Now the extra features, with the first letter capitalized
	echo " (* EXTRAFEATURES: *)" >> $@  
	echo \
          $(foreach f,, \
             "\n   "`echo $f | cut -c 1 | tr "[a-z]" "[A-Z]"``echo $f | cut -c 2-`".feature;") \
	| perl -pe 's/\\n/\n/g' >> $@
	echo "]"   >>$@ 

OCAML_CIL_LIB_MODULES := $(CILLY_LIBRARY_MODULES) 

# Build an OCAML library (CMA / CMXA) that exports our Cil stuff
$(OBJDIR)/cil.$(CMXA): $(OCAML_CIL_LIB_MODULES:%=$(OBJDIR)/%.$(CMO))
	$(CAMLLINK) -a -o $@ -ccopt -L$(pkglibdir) \
               $(OCAML_CIL_LIB_MODULES:%=$(OBJDIR)/%.$(CMO))

# Test cil
ifdef _MSVC
TESTCILARG=--MSVC --testcil "bash msvctestcil"
else
TESTCILARG=       --testcil "bash gcctestcil"
endif

.PHONY: testcil
testcil: $(OBJDIR)/cilly$(EXE)
	       cd test; ../$(OBJDIR)/cilly$(EXE) $(TESTCILARG)

.PHONY: odoc texdoc pdfdoc

###
### DOCUMENTATION
###
### The following are available
###
###   make doc  - creates the documentation

ODOC_FILES =        ocamlutil/pretty.mli ocamlutil/errormsg.mli \
                    ocamlutil/clist.mli \
	  	    ocamlutil/stats.mli src/cil.mli src/formatcil.mli \
                    ocamlutil/alpha.mli src/cillower.mli \
                    src/ext/cfg.mli src/ext/dataflow.mli \
                    src/ext/dominators.mli


# Documentation generated by "ocamldoc"
odoc: $(OBJDIR)/pretty.cmi $(OBJDIR)/cil.cmi
	-rm -rf doc/html/cil/api doc/html/cil/api-latex
	-mkdir -p doc/html/cil/api
	-mkdir -p doc/html/cil/api-latex
	-rm -f doc/ocamldoc.sty
	ocamldoc -d doc/html/cil/api -v -stars \
             -html \
             -t "CIL API Documentation (version 1.4.0)" \
	     -I $(OBJDIR) -hide Pervasives $(ODOC_FILES)

doc/cilpp.tex: doc/cilcode.pl doc/cil.tex lib/Cilly.pm
	-rm -rf doc/html/cil
	-mkdir -p doc/html/cil
	-mkdir -p doc/html/cil/examples
	cd doc; perl cilcode.pl cil.tex >cilpp.tex.tmp
	mv doc/cilpp.tex.tmp $@

# Documentation generated from latex files using "hevea"
texdoc: doc/cilpp.tex
# Create the version document
	cd doc/html/cil; echo "\def\cilversion{1.4.0}" >cil.version.tex
	cd doc/html/cil; echo "\def\ccuredversion{@CCURED_VERSION@}" >>cil.version.tex
	cd doc/html/cil; hevea -exec xxdate.exe ../../cilpp
	cd doc/html/cil; hevea -exec xxdate.exe ../../cilpp
	cd doc/html/cil; mv cilpp.html cil.html
	cd doc/html/cil; hacha -o ciltoc.html cil.html
	cp -f doc/index.html doc/html/cil/index.html
	cp -f doc/header.html doc/html/cil

pdfdoc: doc/cilpp.tex $(OBJDIR)/pretty.cmi $(OBJDIR)/cil.cmi
	cd doc; echo "\def\cilversion{1.4.0}" >cil.version.tex
	cd doc; echo "\def\ccuredversion{@CCURED_VERSION@}" >>cil.version.tex
	cd doc; pdflatex cilpp.tex; pdflatex cilpp.tex
	cd doc; mv cilpp.pdf html/cil/CIL.pdf
	ocamldoc -o doc/cil-api.tex.tmp -v -stars \
             -latex \
             -t "CIL API Documentation (version 1.4.0)" \
	     -I $(OBJDIR) -hide Pervasives $(ODOC_FILES)
	sed -e 's/\\usepackage\[T1\]{fontenc}/\\setlength{\\pdfpagewidth}{\\paperwidth} \\setlength{\\pdfpageheight}{\\paperheight}/' doc/cil-api.tex.tmp >doc/cil-api.tex
	rm doc/cil-api.tex.tmp

	cd doc ; TEXINPUTS="$$TEXINPUTS:/usr/local/lib/ocaml/ocamldoc:/usr/lib/ocaml/ocamldoc" pdflatex cil-api.tex
	cd doc ; mv cil-api.pdf html/cil/CIL-API.pdf


# You should usually run this twice to get all of the references linked
# correctly.
doc: texdoc pdfdoc odoc

distclean: clean
	rm -f src/frontc/cparser.output
	rm -f src/formatparse.output
	rm -f bin/cilly.bat
	rm -f bin/patcher.bat
	rm -f bin/CilConfig.pm
	rm -f config.log
	rm -f config.h
	rm -f Makefile
	rm -f README

cleancheck:
	rm -f test/small1/*.o
	rm -f test/small1/hello
	rm -f test/small1/vararg1
	rm -f test/small1/wchar1

clean: cleancaml cleancheck


# Now include the compiler specific stuff
ifdef _MSVC
  include Makefile.msvc
else
  ifdef _GNUCC
    include Makefile.gcc
  endif
endif

test/%: 
	bin/cilly $(CONLY) test/small1/$*.c $(OBJOUT)test/small1/$*.o

testrun/%: 
	bin/cilly test/small1/$*.c $(OBJOUT)test/small1/$*
	test/small1/$*



.PHONY: quicktest
quicktest: $(patsubst %,test/%,func init init1) \
           $(patsubst %,testrun/%,hello wchar1 vararg1)

.PHONY: check
check: quicktest

########################################################################


INSTALL = /usr/bin/install -c
INSTALL_DATA = ${INSTALL} -m 644

prefix = /usr/local
exec_prefix = ${prefix}
datarootdir = ${prefix}/share
libdir = ${exec_prefix}/lib
pkglibdir = $(libdir)/cil
libexecdir = ${exec_prefix}/libexec
pkglibexecdir = $(libexecdir)/cil
bindir = ${exec_prefix}/bin
# Hack - I don't know how to find perl_vendorlib
perl_vendorlib = $(libdir)

install: install-findlib
	$(INSTALL) -d $(DESTDIR)$(perl_vendorlib)
	$(INSTALL) -m 0644 lib/*.pm bin/CilConfig.pm $(DESTDIR)$(perl_vendorlib)
	sed -i -e "s,^.*cilhome.*$$,\$$::cilhome = \"$(libexecdir)/cil\";," $(DESTDIR)$(perl_vendorlib)/CilConfig.pm
	$(INSTALL) -d $(DESTDIR)$(bindir)
	$(INSTALL) -m 0755 bin/cilly $(DESTDIR)$(bindir)
	$(INSTALL) -d $(DESTDIR)$(pkglibexecdir)/$(OBJDIR)
	$(INSTALL) -m 0755 $(OBJDIR)/cilly.*.exe $(DESTDIR)$(pkglibexecdir)/$(OBJDIR)

# FIXME
uninstall: uninstall-findlib
	-rm -rf $(DESTDIR)$(pkglibexecdir)
	-rm -f $(DESTDIR)$(bindir)/cilly
	-rm -f $(DESTDIR)$(perl_vendorlib)/CilConfig.pm $(DESTDIR)$(perl_vendorlib)/Cilly.pm \
	   	$(DESTDIR)$(perl_vendorlib)/KeptFile.pm $(DESTDIR)$(perl_vendorlib)/OutputFile.pm \
		$(DESTDIR)$(perl_vendorlib)/TempFile.pm

EVERYTHING=$(OBJDIR)/*.cmi $(OBJDIR)/*.cmx $(OBJDIR)/*.cma $(OBJDIR)/*.mli \
		   $(OBJDIR)/*.cmxa $(OBJDIR)/*.a $(SOURCEDIRS:%=%/*.mli)

install-findlib: all $(EVERYTHING)
	ocamlfind install cil META $(EVERYTHING)

uninstall-findlib:
	ocamlfind remove cil
